#ifndef __M_ROUTE_H__
#define __M_ROUTE_H__
#include <iostream>
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"

namespace wsmq
{
    class Router
    {
    public:
        static bool IsLegalRoutingKey(const std::string& routing_key)
        {
            // 只需要判断是否存在非法字符，合法字符：'0'-'9','a'-'z','A'-'Z',.,_
            for(auto ch : routing_key)
            {
                if((ch >= '0' && ch <= '9') ||
                    (ch >= 'a' && ch <= 'z') ||
                    (ch >= 'A' && ch <= 'Z') ||
                    ch == '.' || ch == '_')
                {
                    continue;    
                }
                else
                {
                    return false;
                }
            }
            return true;
        }
        static bool IsLegalBindingKey(const std::string& binding_key)
        {
            // 判断是否存在非法字符，合法字符：'0'-'9','a'-'z','A'-'Z', ., _, #, *
            for(auto ch : binding_key)
            {
                if((ch >= '0' && ch <= '9') ||
                    (ch >= 'a' && ch <= 'z') ||
                    (ch >= 'A' && ch <= 'Z') ||
                    ch == '.' || ch == '_' || 
                    ch == '#' || ch == '*')
                {
                    continue;    
                }
                else
                {
                    return false;
                }
            }
            // 还需要保证#和*不能和单词同时出现
            std::vector<std::string> words;
            StrHelper::split(binding_key, ".", words);
            for(auto& word : words)
            {
                if(word.size() > 1 && 
                (word.find('#') != std::string::npos ||
                 word.find('*') != std::string::npos))
                {
                    return false;
                }
            }
            // 最后，还必须保证*不能出现在#的左边和右边
            for(int i = 1; i < words.size(); i++)
            {
                if(words[i] == "#" && words[i - 1] == "*")
                {
                    return false;
                }
                if(words[i] == "*" && words[i - 1] == "#")
                {
                    return false;
                }
                if(words[i] == "#" && words[i - 1] == "#")
                {
                    return false;
                }
            }
            return true;
        }
        static bool route(ExchangeType type, const std::string& routing_key, const std::string& binding_key)
        {
            if(type == ExchangeType::FANOUT) // 广播交换
            {
                return true;
            }
            else if(type == ExchangeType::DIRECT) // 直接交换
            {
                return (routing_key == binding_key);
            }
            // 根据匹配规则交换，动态规划的算法思想实现：news.# & news.music.pop
            // 将binding_key与routing_key按.分割为单词数组bkeys、rkeys
            std::vector<std::string> bkeys, rkeys;
            int bkey_cnt = StrHelper::split(binding_key, ".", bkeys);
            int rkey_cnt = StrHelper::split(routing_key, ".", rkeys);

            // 状态标识：定义dp数组，dp[i][j]的含义为binding_key的第i个位置和routing_key的第j个位置是否匹配成功
            // 状态转移方程：bkey为*或两个单词相同时，左上方继承结果；bkey为#时，从左上、上方、左方继承结果
            // *能和任意单词匹配
            // 初始化：多初始化一行一列则要注意映射关系，dp[0][0]设置为true，#所在行的第0列应初始化为true
            // 填表顺序：从左到右，从上到下
            // 返回值：dp[bkey_cnt][rkey_cnt]
            std::vector<std::vector<bool>> dp(bkey_cnt + 1, std::vector<bool>(rkey_cnt + 1, false));
            dp[0][0] = true;
            for(int i = 1; i < bkey_cnt; i++)
            {
                if(bkeys[i - 1] == "#") // dp多开一行一列的映射关系
                {
                    dp[i][0] = true;
                }
            }
            for(int i = 1; i <= bkey_cnt; i++)
            {
                for(int j = 1; j <= rkey_cnt; j++)
                {
                    if(bkeys[i - 1] == rkeys[j - 1] || bkeys[i - 1] == "*")
                    {
                        dp[i][j] = dp[i - 1][j - 1];
                    }
                    else if(bkeys[i - 1] == "#")
                    {
                        dp[i][j] = dp[i - 1][j - 1] || dp[i][j - 1] || dp[i - 1][j];
                    }
                }
            }
            return dp[bkey_cnt][rkey_cnt];
        }
    };

}

#endif